package edu.pku.sei.autounit.reflect.java;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.HashMap;
import java.util.Map;

import edu.pku.sei.autounit.reflect.Context;
import edu.pku.sei.autounit.reflect.Type;
import edu.pku.sei.autounit.reflect.TypeParameter;
import edu.pku.sei.autounit.util.Log;

public class TypeFactory {

	protected Map<String,Type> map ;
	
	public TypeFactory(){
		map = new HashMap<String,Type>();
		map.put("boolean", new JavaPrimitive("boolean"));
		map.put("byte", new JavaPrimitive("byte"));
		map.put("char", new JavaPrimitive("char"));
		map.put("short", new JavaPrimitive("short"));
		map.put("int", new JavaPrimitive("int"));
		map.put("long", new JavaPrimitive("long"));
		map.put("float", new JavaPrimitive("float"));
		map.put("double", new JavaPrimitive("double"));
		Type voidType = new Type(){
			
			public String getFullName() {
				return "void";
			}

			
			public String getName(Map<String, String> map) {
				return "void";
			}

			
			public boolean isAssignableFrom(Type type) {
				return false;
			}};
		map.put("void", voidType);
		map.put("Void", voidType);
	}
	
	public Type getType(java.lang.reflect.Type type,Context context){
		if(context == null){
			context = new Context(){

				
				public Type[] getContexts() {
					return new Type[0];
				}

				
				public TypeParameter getTypeParameter(String name) {
					return null;
				}};
		}
		if(type instanceof GenericArrayType){
			GenericArrayType gt = (GenericArrayType) type;
			Type elementType = getType(gt.getGenericComponentType(),context);
			return new JavaArray(elementType);
		}
		else if(type instanceof ParameterizedType){
			if(((Class<?>)((ParameterizedType) type).getRawType()).isInterface())
				return new JavaInterface((ParameterizedType)type,context,this);
			else
				return new JavaClass((ParameterizedType)type,context,this);
		}
		else if(type instanceof Class<?>){
			Class<?> c = (Class<?>) type;
			if(c.isArray()){
				Type elementType = getType(c.getComponentType(),context);
				return new JavaArray(elementType);
			}
			else if(c.isPrimitive())
				return new JavaPrimitive(c.getName());
			else if(c.isInterface())
				return new JavaInterface(c,this);
			else if(c.isEnum()){
				return new JavaEnum(c);
			}
			else if(c.isAnnotation()){
				//TODO 
			}
			else
				return new JavaClass(c,this);
		}
		else if(type instanceof TypeVariable<?>){
			String name = ((TypeVariable<?>)type).getName();
			TypeParameter p = context.getTypeParameter(name);
			if(p == null)
				return new JavaTypeVariable(name,new Type[0]);
			else if(p.getContext()!=null)
				return p.getContext();
			else
				return new JavaTypeVariable(name,p.getBounds());
		}
		else if(type instanceof WildcardType){
			WildcardType et = (WildcardType) type;
			Type lower = null;
			Type upper = null;
			if(et.getLowerBounds().length!=0){
				lower = getType(et.getLowerBounds()[0],context);
			}
			if(et.getUpperBounds().length!=0){
				upper = getType(et.getUpperBounds()[0],context);
			}
			return new JavaWildcardType(lower,upper);
		}
		Log.findBug("TypeFactory", "getType", "unknown type:"+type.toString());
		return null;
	}
}
